package com.blackcat.aspect;

import com.alibaba.fastjson.JSON;
import com.blackcat.entity.HandleLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * <p> 描述 : 日志切面
 * @author : zhangdahui
 * @date  : 2020/12/7 16:27
 *
 * Component：泛指组件
 * Aspect：声明一个切面
 */
@Component
@Aspect
@Slf4j
public class LogAspect {

	// 操作日志对象
	ThreadLocal<HandleLog> handleLogLocal = new ThreadLocal<>();

	/**
	 * <p> 描述 : 定义一个切点，避免重复引用
	 * 	每个Pointcut的定义包括2部分，一是表达式，二是方法签名。方法签名必须是 public及void型。
	 * @author : blackcat
	 * @date  : 2020/12/7 16:35
	*/
	@Pointcut("execution(public * com.blackcat.controller.TestController.*(..))")
	public void handleLog(){}

	/**
	 * <p> 描述 : 在切点方法之前执行
	 * 	标识一个前置增强方法，相当于BeforeAdvice的功能。
	 * @author : blackcat
	 * @date  : 2020/12/7 16:35
	 */
	@Before("handleLog()")
	public void doBefore(JoinPoint joinPoint) throws Throwable {
		// 接收请求，对参数进行非空校验
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		HandleLog handleLog = new HandleLog();
		handleLog.setIp(request.getRemoteAddr());
		handleLogLocal.set(handleLog);
		log.info("doBefore");
		log.info("URL路径 : " + request.getRequestURL().toString());
		log.info("请求方式 : " + request.getMethod());
		log.info("ip : " +request.getRemoteAddr());
		log.info("请求路径 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
		log.info("请求参数 : " + Arrays.toString(joinPoint.getArgs()));
		log.info("方法名称 : " + joinPoint.getSignature().getName());
	}

	/**
	 * <p> 描述 : 切点方法返回后执行。
	 * 		后置增强，相当于AfterReturningAdvice，方法正常退出时执行。
	 * @author : blackcat
	 * @date  : 2020/12/7 16:35
	 */
	@AfterReturning(returning = "object", pointcut = "handleLog()")
	public void doAfterReturning(Object object) throws Throwable {
		log.info("RESPONSE : " + object);
		HandleLog handleLog = handleLogLocal.get();
		// 处理完请求，返回内容
		handleLog.setResponseParam(JSON.toJSONString(object));
		// 使用完毕清理对象
		handleLogLocal.remove();

		log.info("返回值 : " + JSON.toJSONString(object));
	}

	/**
	 * <p> 描述 : 切点方法抛异常执行。异常抛出增强，相当于ThrowsAdvice。
	 * @author : blackcat
	 * @date  : 2020/12/7 16:35
	 */
	@AfterThrowing(throwing="exception",pointcut="execution(public * com.blackcat.controller.TestController.*(..))")
	public void afterThrowing(JoinPoint joinPoint,Exception exception){
		// 异常信息存储
		HandleLog handleLog = handleLogLocal.get();
		handleLog.setException(exception.toString());
		// 使用完毕清理对象
		handleLogLocal.remove();
		log.info(exception.toString());
	}
}
